package com.qf.push.server;

import com.alibaba.fastjson.JSONObject;
import com.qf.base.util.SpringContextUtil;
import com.qf.push.dao.MessageDao;
import com.qf.push.model.Message;
import com.qf.push.model.MessageExample;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 汤奇
 * @version V1.0
 * @Project BookPro
 * @Package com.qf.push.server
 * @Description: WebSocket的服务器
 * @Date 2022/8/8 17:48
 */
@ServerEndpoint("/webSocket/{uid}")
@Component
public class WebSocketServer {
    /**
     * 这里使用线程安全的数据类型
     * 因为session的操作场景是多线程并发
     */
    private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<>();

    private MessageDao messageDao;

    /**
     * 这个方法是当前端与服务器产生websocket连接的时候, 这个方法就运行
     * 专业说法: webSocket连接建立的回调
     * @param session   代表了这个连接会话
     * @param uid          用户的id
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("uid") String uid){
        sessionPool.put(uid,session);
        if (messageDao == null){
            messageDao = SpringContextUtil.getBean(MessageDao.class);
        }
        //样例对象
        MessageExample example = new MessageExample();
        //条件对象
        MessageExample.Criteria criteria = example.createCriteria();
        //设置uid为条件
        criteria.andUidEqualTo(Long.valueOf(uid));
        //设置state为条件
        criteria.andStateEqualTo(0);
        //设置过期时间为条件
        criteria.andLastTimeEqualTo(System.currentTimeMillis());
        //设置排序
        example.setOrderByClause("id desc");
        //执行查询
        List<Message> messages = messageDao.selectByExample(example);

        //把这些消息都推送给前端
        List<JSONObject> msgs = new ArrayList<>();

        for (int i = 0; i < messages.size(); i++) {
            Message message = messages.get(i);
            JSONObject msg = new JSONObject();
            msg.put("uid",message.getUid());
            msg.put("serviceId",message.getServiceId());
            //content有可能是json, 应该避免json套json导致\成指数趋势增加
            msg.put("content",JSONObject.parseObject(message.getContent()));
            msgs.add(msg);
        }
        //整个是一个报文, 推送至前端
        JSONObject info = new JSONObject();
        info.put("connect",true);
        info.put("message",msgs);
        //推送
        sendMessage(session,JSONObject.toJSONString(info));
        //更新数据库
        Message msg = new Message();
        //设置未推送的信息的状态为已推送
        msg.setState(1);
        messageDao.updateByExampleSelective(msg,example);
    }

    /**
     * 当对方发送过来信息的时候, 这个方法就运行
     * 专业的说法: 接收信息的回调
     * @param message   接收的信息
     */
    @OnMessage
    public void onMessage(String message){
        System.out.println(message);
    }

    /**
     * 当连接关闭的时候, 这个方法就运行
     * @param uid
     */
    @OnClose
    public void onClose(@PathParam("uid") String uid){
        sessionPool.remove(uid);
    }

    /**
     * 发生错误时回调
     * @param session   发生错误的连接的会话对象
     * @param throwable 发生的错误类型
     */
    @OnError
    public void onError(Session session, Throwable throwable){
        throwable.printStackTrace();
    }

    /**
     * 发送信息的方法
     * @param session
     * @param message
     */
    private void sendMessage(Session session,String message){
        if (session != null){
            synchronized (session){
                try {
                    session.getBasicRemote().sendText(message);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 对外界提供根据用户id发送信息的方法
     * @param uid
     * @param message
     * @return
     */
    public boolean sendInfo(String uid, String message){
        Session session = sessionPool.get(uid);
        if (session == null){
            return false;
        } else {
            sendMessage(session,message);
            return true;
        }
    }
}
